package jmine.tec.web.wicket.component.table;

import images.ImageResources;

import java.util.List;
import java.util.Map;

import jmine.tec.report.api.Report;
import jmine.tec.report.api.ReportElement;
import jmine.tec.report.api.table.ReportCell;
import jmine.tec.report.api.table.ReportRow;
import jmine.tec.report.api.table.ReportTable;
import jmine.tec.report.export.ReportFileExporter;
import jmine.tec.report.impl.ReportBuilder;
import jmine.tec.report.impl.table.ReportCellImpl;
import jmine.tec.services.api.export.DefaultExportAction;
import jmine.tec.services.api.export.ExportActor;
import jmine.tec.web.wicket.BeanReference;
import jmine.tec.web.wicket.component.command.TableCommandsProvider;
import jmine.tec.web.wicket.component.command.button.SearchCommandHandler;
import jmine.tec.web.wicket.component.command.img.ImageCommandsPanel;
import jmine.tec.web.wicket.component.paging.PagingNavigatorContainer;
import jmine.tec.web.wicket.component.report.ExportReportWebResource;
import jmine.tec.web.wicket.component.report.ReportTableMarkupBuilder;
import jmine.tec.web.wicket.component.table.dataprovider.DataProviderConverter;
import jmine.tec.web.wicket.component.table.dataprovider.SearchHandlerDataProvider;
import jmine.tec.web.wicket.component.table.resource.ServiceXLSWebResource;
import jmine.tec.web.wicket.spring.SpringBeanReference;

import org.apache.wicket.Component;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.DataGridView;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.link.ResourceLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.IDataProvider;
import org.apache.wicket.model.IModel;

import bancosys.tec.persist.dao.BaseDAOFactory;
import bancosys.tec.persist.hibernate.page.PagingService;

/**
 * {@link SearchResultTableBuilder} para criar o markup de um {@link Report}
 * 
 * @author Rafael Volpato
 * @param <BO> tipo da entidade
 */
public class SearchResultTableBuilder<BO> extends ReportTableMarkupBuilder {

    private static final int RESULTS_PER_PAGE = 20;

    private final BeanReference<ExportActor> exportActor = SpringBeanReference.forName("exportActor");

    private final BeanReference<BaseDAOFactory> daoFactory = SpringBeanReference.forName("daoFactory");

    private final BeanReference<PagingService> pagingService = SpringBeanReference.forName("pagingService");

    private final TableCommandsProvider commandsProvider;

    private final SearchCommandHandler<BO> searchHandler;

    private final Class<BO> targetClass;

    private int currentPage;

    /**
     * Construtor.
     * 
     * @param targetClass targetClass
     * @param searchHandler {@link SearchCommandHandler}
     * @param commandsProvider {@link TableCommandsProvider}
     */
    public SearchResultTableBuilder(Class<BO> targetClass, SearchCommandHandler<BO> searchHandler, TableCommandsProvider commandsProvider) {
        this.targetClass = targetClass;
        this.commandsProvider = commandsProvider;
        this.searchHandler = searchHandler;
    }

    /**
     * Cria um componente com o markup inlined. Para se ter uma ideia de como deve ser feito o layout do painel, olhar ReportTablePanel.html
     * 
     * @param id {@link String}
     * @param report {@link Report}
     * @return {@link Component}
     */
    @Override
    public WebMarkupContainer createInlineMarkup(String id, Report report) {
        WebMarkupContainer mainContainer = super.createInlineMarkup(id, report);
        mainContainer.add(this.createExportServiceXLSLink(report));
        return mainContainer;
    }

    /**
     * Cria o body de uma tabela.
     * 
     * @param item item aonde a tabela deve ser adicionada
     * @param table {@link ReportTable}
     * @return {@link Component}
     */
    @Override
    protected DataGridView<ReportRow> createAndAddBody(ListItem<ReportElement> item, ReportTable<?> table) {
        DataGridView<ReportRow> grid = super.createAndAddBody(item, table);
        grid.setRowsPerPage(RESULTS_PER_PAGE);
        grid.setCurrentPage(this.currentPage);

        PagingNavigatorContainer pagingNavigator = new PagingNavigatorContainer("navigator", new PageableAdapter(grid));
        pagingNavigator.setOutputMarkupId(true);
        item.add(pagingNavigator);

        return grid;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected IDataProvider<ReportRow> toDataProvider(ReportTable<?> table) {
        SearchHandlerDataProvider<BO> dataProvider =
                new SearchHandlerDataProvider<BO>(RESULTS_PER_PAGE, this.daoFactory, this.pagingService, this.searchHandler);
        return new DataProviderConverter<BO>(dataProvider, table);
    }

    /**
     * Cria um link para exportacao de serviços em XLS
     * 
     * @param report {@link Report}
     * @return {@link Component}
     */
    private Component createExportServiceXLSLink(final Report report) {
        final ServiceXLSWebResource resource = new ServiceXLSWebResource(this.exportActor, report.getHeader().getFileName());
        Link<Void> link = new ResourceLink<Void>("exportService", resource) {
            @Override
            public void onClick() {
                List<BO> result = SearchResultTableBuilder.this.searchHandler.search(SearchResultTableBuilder.this.daoFactory.get());
                resource.setResult(result);
            }
        };
        link.add(new Image("icon", new ResourceReference(ImageResources.class, "serviceicon.png")));
        link.setVisible(this.isIncludeHeaders() && this.exportActor.get().canExport(DefaultExportAction.INCLUIR, this.targetClass));
        return link;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Link<Void> createResourceLink(String id, SpringBeanReference<? extends ReportFileExporter> reportExporter, final Report report) {
        return new ResourceLink<Void>(id, new ExportReportWebResource(reportExporter, report)) {
            @Override
            @SuppressWarnings("unchecked")
            public void onClick() {
                super.onClick();

                List<BO> result = SearchResultTableBuilder.this.searchHandler.search(SearchResultTableBuilder.this.daoFactory.get());

                // faz o reset dos parametros do cabeçalho para não ocorrer duplicidade.
                report.getHeader().clearParameters();

                SearchResultTableBuilder.this.searchHandler.addReportHeader(new ReportBuilder(report));
                for (ReportElement element : report.getElements()) {
                    if (element instanceof ReportTable) {
                        ((ReportTable) element).setRecords(result);
                    }
                }
            }
        };
    }

    /**
     * cria uma lista com {@link ICellPopulator} para as linhas da tabela
     * 
     * @param <V> o tipo da tabela
     * @param table {@link ReportTable}
     * @return {@link List}
     */
    @Override
    @SuppressWarnings("unchecked")
    protected <V> List<ICellPopulator<ReportRow>> getCellPopulators(ReportTable<V> table) {
        List<ICellPopulator<ReportRow>> cellPopulators = super.getCellPopulators(table);

        if (this.commandsProvider.shouldIncludeTableCommands()) {
            cellPopulators.add(new ICellPopulator<ReportRow>() {
                public void detach() {
                }

                public void populateItem(Item<ICellPopulator<ReportRow>> cellItem, String componentId, IModel<ReportRow> rowModel) {
                    Map<String, ReportCell> cellMap = rowModel.getObject().getCellMap();
                    ReportCell cellValue = cellMap.get("pk");
                    Long pk = Long.valueOf(String.valueOf(cellValue.getValue()));
                    ImageCommandsPanel commandsPanel =
                            new ImageCommandsPanel(componentId, pk, SearchResultTableBuilder.this.commandsProvider.getTableCommands(pk));
                    cellItem.add(commandsPanel);
                }
            });
        }

        return cellPopulators;
    }

    /**
     * Cria um header
     * 
     * @param reportElement {@link ReportTable}
     * @return {@link Component}
     */
    @Override
    protected ListView<ReportCell> createColumnHeader(ReportTable reportElement) {
        ListView<ReportCell> lvCells = super.createColumnHeader(reportElement);

        if (this.commandsProvider.shouldIncludeTableCommands()) {
            List<ReportCell> reportCells = lvCells.getModelObject();
            reportCells.add(new ReportCellImpl("Ações"));
            lvCells.setModelObject(reportCells);
        }

        return lvCells;
    }

    /**
     * {@link IPageable} que mantem o estado sincronizado entre a instancia the {@link SearchResultTableBuilder} e um {@link IPageable}
     * 
     * @author takeshi
     */
    private class PageableAdapter implements IPageable {

        private final IPageable delegate;

        /**
         * Ctor
         * 
         * @param delegate {@link IPageable}
         */
        public PageableAdapter(IPageable delegate) {
            this.delegate = delegate;
        }

        /**
         * {@inheritDoc}
         */
        public int getCurrentPage() {
            return SearchResultTableBuilder.this.currentPage;
        }

        /**
         * {@inheritDoc}
         */
        public void setCurrentPage(int page) {
            SearchResultTableBuilder.this.currentPage = page;
            this.delegate.setCurrentPage(page);
        }

        /**
         * {@inheritDoc}
         */
        public int getPageCount() {
            return this.delegate.getPageCount();
        }
    }
}
